#ifdef __GNUG__
#pragma implementation
#endif

#include "SunIAC.h"

#include "Class.h"
#include "Error.h"
#include "String.h"
#include "OrdColl.h"
#include "File.h"
#include "System.h"
#include "ET_stdio.h"

const int cHeaderSize= 20;

//---- block -------------------------------------------------------------------

block::block()
{
	len= cHeaderSize;
	pos= 0;
	buf= 0;
}

block::~block()
{
	len= pos= 0;
	SafeDelete(buf);
}

//---- SunMsg ------------------------------------------------------------------

SunMsg::SunMsg()
{
	to= from= 0;
	serial= 0;
}

SunMsg::SunMsg(int t, int f, int reply, const char *buf, int l, bool makecopy)
{
	to= t;
	from= f;
	serial= reply;
	if (l < 0)
		l= strlen(buf)+1;
	data[1].len= l;
	if (makecopy) {
		data[1].buf= new char[l];
		strncpy(data[1].buf, buf, l);
	} else
		data[1].buf= (char*)buf;
}

void SunMsg::Prepare()
{
	SafeDelete(data[0].buf);
	data[0].len= cHeaderSize;
	data[0].pos= 0;
	data[0].buf= new char[cHeaderSize];
	data[1].pos= 0;
	sprintf(data[0].buf, "%d %d %d %d", to, from, data[1].len, serial);
}

void SunMsg::SetFrom(int fr)
{
	from= fr;
}

int SunMsg::Read(int fd)
{
	for (int i= 0; i < 2; i++) {
		block *b= &data[i];
		
		while (b->pos < b->len) {
			if (b->buf == 0)
				b->buf= new char[b->len];
			int cnt= File::Read(fd, b->buf+b->pos, b->len-b->pos);

			if (cnt == 0 || cnt == -1)
				return -1;

			if (cnt == -2)
				return -2;

			b->pos+= cnt;
		
			if (b->pos >= b->len) {
				if (i == 1)
					return 0;

				if (sscanf(b->buf, "%d %d %d %d",
					&to, &from, &data[1].len, &serial) != 4)
					return -1;
			}
		}
	}
	return 0;
}

int SunMsg::Write(int fd)
{
	for (int i= 0; i < 2; i++) {
		block *b= &data[i];
		
		while (b->pos < b->len) {
			int wl= File::Write(fd, b->buf+b->pos, b->len-b->pos);
			fprintf(stderr, "w:%d\n", wl);
			if (wl == -1)   // fatal error
				return -1;
			if (wl == -2)   // try again
				return -2;
			b->pos+= wl;
		}
	}
	Print("Written:");
	return 0;                   // completely written
}

void SunMsg::Print(char *msg)
{
	if (gIACDebug) {
		int l= data[1].len;
		
		fprintf(stderr, "%s fr:%d to:%d ser:%d l:%d <", msg, from, to, serial, l);
		if (l < 60) {
			fprintf(stderr, "%s>\n", data[1].buf);
		} else {
			fwrite(data[1].buf, 1, 60, stderr);
			fprintf(stderr, "...>\n");
		}
	}
}

//---- SunIACHandler -----------------------------------------------------------

class SunIACHandler : public FileHandler {
public:
	SunIACHandler(SunIAC *r, int fd) : FileHandler(fd, 3)
		{ rpc= r; }
	~SunIACHandler()
		{ File::Close(GetFd()); }
	bool ReadNotify()
		{ return rpc->ReadNotify(); }
	bool WriteNotify()
		{ return rpc->WriteNotify(); }
	bool HasWriteInterest()
		{ return rpc->HasWriteInterest(); }

protected:
	SunIAC *rpc;
};

//---- SunIAC ------------------------------------------------------------------

SunIAC::SunIAC(IAC *s) : IACImpl(s)
{
}

SunIAC::SunIAC(int fd) : IACImpl((IAC*)0)
{
	Init(fd);
}

int SunIAC::Open(const char *nm)
{
	int fd= gSystem->OpenConnection(SERVERNAME, SERVICENAME);
	if (fd > 0) {
		Init(fd);
		if (sc) {
			sc->Broadcast("enroll", nm);
			return LookupName(nm, FALSE);
		}
	} else
		sc= 0;
	return -1;
}

void SunIAC::Init(int fd)
{
	rm= 0;
	wqueue= new OrdCollection;

	File::SetNonBlock(fd);
	handler= new SunIACHandler(this, fd);
	gSystem->AddFileHandler(handler);
}

int SunIAC::Close(const char *nm)
{
	if (sc)
		return sc->Broadcast("remove", nm);
	return -1;
}

SunIAC::~SunIAC()
{
	SafeRemove(handler);
	SafeDelete(wqueue);
}

void SunIAC::Dispatch(SunMsg *m)
{
	if (m->Serial() < 0)
		SendUp(TRUE, m->From(), -m->Serial(), m->Data(), m->Len());
	else
		SendUp(FALSE, m->From(), m->Serial(), m->Data(), m->Len());
}

int SunIAC::LookupName(const char *nm, bool start)
{
	char *rbuf= 0;
	int id= -1;
	if (sc)
		sc->Talk(1, start ? "start" : "findname", (char*)nm, -1, &rbuf);
	if (rbuf && sscanf(rbuf, "%d", &id) != 1)
		id= -1;
	//SafeDelete(rbuf);
	return id;
}

int SunIAC::SendTo(int to, int ser, const char *buf, int len)
{
	return SendMessage(new SunMsg(to, 0, ser, buf, len, TRUE));
}

int SunIAC::Broadcast(int ser, const char *buf, int len)
{
	return SendMessage(new SunMsg(0, 0, ser, buf, len, TRUE));
}

int SunIAC::Poll(int ser, const char *buf, int len)
{
	return SendMessage(new SunMsg(2, 0, ser, buf, len, TRUE));
}

int SunIAC::SendReply(int to, int ser, int, const char *buf, int len)
{
	if (ser > 0)
		return SendMessage(new SunMsg(to, 0, -ser, buf, len, TRUE));
	return -1;
}

int SunIAC::SendMessage(SunMsg *m)
{
	//m->Print("SendMessage");
	m->Prepare();
	wqueue->Add(m);
	return 0;
}

bool SunIAC::HasWriteInterest()
{
	return wqueue && wqueue->Size() > 0;
}

bool SunIAC::WriteNotify()
{
	if (GetError() > 0)
		return FALSE;
	SunMsg *m= (SunMsg*) wqueue->First();
	switch (m->Write(handler->GetFd())) {
	case 0:     // completely written
		if ((SunMsg*)wqueue->RemovePtr(m) == m) {
			SafeDelete(m);
		} else  // inconsistent queue
			SendUpError(5);
		break;
	case -1:    // fatal error
		SendUpError(4);
		break;
	case -2:    // try again
		break;
	}
	return TRUE;
}

bool SunIAC::ReadNotify()
{
	if (GetError() > 0)
		return FALSE;
	if (rm == 0)
		rm= new SunMsg;

	switch (rm->Read(handler->GetFd())) {
	case 0:     // got one, process it
		if (rm->From() == 0)
			rm->SetFrom(sid);
		rm->Print("Got:");
		Dispatch(rm);
		rm= 0;
		break;
	case -1:    // fatal error
		SendUpError(2);
		break;
	case -2:    // try again later
		break;
	}
	return TRUE;
}

